; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
; RUN: opt -S -passes="guard-widening,make-guards-explicit,simplifycfg" < %s        | FileCheck %s --check-prefixes=INTRINSIC_FORM
; RUN: opt -S -passes="make-guards-explicit,guard-widening,simplifycfg" < %s        | FileCheck %s --check-prefixes=BRANCH_FORM
; RUN: opt -S -passes="make-guards-explicit,loop-mssa(licm),guard-widening,simplifycfg" < %s        | FileCheck %s --check-prefixes=BRANCH_FORM_LICM

declare i1 @cond() readonly

; FIXME We want to make sure that guard widening works in the same way, no matter what form of
;       guards it is dealing with.
; We also want to make sure that LICM doesn't mess with widenable conditions, what might
; make things more complex.

define void @test_01(i32 %a, i32 %b, i32 %c, i32 %d) {
; INTRINSIC_FORM-LABEL: define void @test_01
; INTRINSIC_FORM-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], i32 [[D:%.*]]) {
; INTRINSIC_FORM-NEXT:  entry:
; INTRINSIC_FORM-NEXT:    [[D_GW_FR:%.*]] = freeze i32 [[D]]
; INTRINSIC_FORM-NEXT:    [[C_GW_FR:%.*]] = freeze i32 [[C]]
; INTRINSIC_FORM-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B]]
; INTRINSIC_FORM-NEXT:    br label [[LOOP:%.*]]
; INTRINSIC_FORM:       loop:
; INTRINSIC_FORM-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ]
; INTRINSIC_FORM-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; INTRINSIC_FORM-NEXT:    [[C1:%.*]] = icmp ult i32 [[IV]], [[A]]
; INTRINSIC_FORM-NEXT:    [[C2:%.*]] = icmp ult i32 [[IV]], [[B_GW_FR]]
; INTRINSIC_FORM-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
; INTRINSIC_FORM-NEXT:    [[C3:%.*]] = icmp ult i32 [[IV]], [[C_GW_FR]]
; INTRINSIC_FORM-NEXT:    [[WIDE_CHK1:%.*]] = and i1 [[WIDE_CHK]], [[C3]]
; INTRINSIC_FORM-NEXT:    [[C4:%.*]] = icmp ult i32 [[IV]], [[D_GW_FR]]
; INTRINSIC_FORM-NEXT:    [[WIDE_CHK2:%.*]] = and i1 [[WIDE_CHK1]], [[C4]]
; INTRINSIC_FORM-NEXT:    [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; INTRINSIC_FORM-NEXT:    [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK2]], [[WIDENABLE_COND]]
; INTRINSIC_FORM-NEXT:    br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0:![0-9]+]]
; INTRINSIC_FORM:       deopt:
; INTRINSIC_FORM-NEXT:    call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
; INTRINSIC_FORM-NEXT:    ret void
; INTRINSIC_FORM:       guarded:
; INTRINSIC_FORM-NEXT:    [[LOOP_COND:%.*]] = call i1 @cond()
; INTRINSIC_FORM-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; INTRINSIC_FORM:       exit:
; INTRINSIC_FORM-NEXT:    ret void
;
; BRANCH_FORM-LABEL: define void @test_01
; BRANCH_FORM-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], i32 [[D:%.*]]) {
; BRANCH_FORM-NEXT:  entry:
; BRANCH_FORM-NEXT:    [[D_GW_FR:%.*]] = freeze i32 [[D]]
; BRANCH_FORM-NEXT:    [[C_GW_FR:%.*]] = freeze i32 [[C]]
; BRANCH_FORM-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B]]
; BRANCH_FORM-NEXT:    br label [[LOOP:%.*]]
; BRANCH_FORM:       loop:
; BRANCH_FORM-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ]
; BRANCH_FORM-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; BRANCH_FORM-NEXT:    [[C1:%.*]] = icmp ult i32 [[IV]], [[A]]
; BRANCH_FORM-NEXT:    [[C2:%.*]] = icmp ult i32 [[IV]], [[B_GW_FR]]
; BRANCH_FORM-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
; BRANCH_FORM-NEXT:    [[C3:%.*]] = icmp ult i32 [[IV]], [[C_GW_FR]]
; BRANCH_FORM-NEXT:    [[WIDE_CHK13:%.*]] = and i1 [[WIDE_CHK]], [[C3]]
; BRANCH_FORM-NEXT:    [[C4:%.*]] = icmp ult i32 [[IV]], [[D_GW_FR]]
; BRANCH_FORM-NEXT:    [[WIDE_CHK14:%.*]] = and i1 [[WIDE_CHK13]], [[C4]]
; BRANCH_FORM-NEXT:    [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM-NEXT:    [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK14]], [[WIDENABLE_COND]]
; BRANCH_FORM-NEXT:    br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0:![0-9]+]]
; BRANCH_FORM:       deopt:
; BRANCH_FORM-NEXT:    call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
; BRANCH_FORM-NEXT:    ret void
; BRANCH_FORM:       guarded:
; BRANCH_FORM-NEXT:    [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM-NEXT:    [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[C2]], [[WIDENABLE_COND3]]
; BRANCH_FORM-NEXT:    [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM-NEXT:    [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[C3]], [[WIDENABLE_COND7]]
; BRANCH_FORM-NEXT:    [[WIDENABLE_COND11:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM-NEXT:    [[EXIPLICIT_GUARD_COND12:%.*]] = and i1 [[C4]], [[WIDENABLE_COND11]]
; BRANCH_FORM-NEXT:    [[LOOP_COND:%.*]] = call i1 @cond()
; BRANCH_FORM-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; BRANCH_FORM:       exit:
; BRANCH_FORM-NEXT:    ret void
;
; BRANCH_FORM_LICM-LABEL: define void @test_01
; BRANCH_FORM_LICM-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], i32 [[D:%.*]]) {
; BRANCH_FORM_LICM-NEXT:  entry:
; BRANCH_FORM_LICM-NEXT:    [[D_GW_FR:%.*]] = freeze i32 [[D]]
; BRANCH_FORM_LICM-NEXT:    [[C_GW_FR:%.*]] = freeze i32 [[C]]
; BRANCH_FORM_LICM-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B]]
; BRANCH_FORM_LICM-NEXT:    br label [[LOOP:%.*]]
; BRANCH_FORM_LICM:       loop:
; BRANCH_FORM_LICM-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ]
; BRANCH_FORM_LICM-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; BRANCH_FORM_LICM-NEXT:    [[C1:%.*]] = icmp ult i32 [[IV]], [[A]]
; BRANCH_FORM_LICM-NEXT:    [[C2:%.*]] = icmp ult i32 [[IV]], [[B_GW_FR]]
; BRANCH_FORM_LICM-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
; BRANCH_FORM_LICM-NEXT:    [[C3:%.*]] = icmp ult i32 [[IV]], [[C_GW_FR]]
; BRANCH_FORM_LICM-NEXT:    [[WIDE_CHK13:%.*]] = and i1 [[WIDE_CHK]], [[C3]]
; BRANCH_FORM_LICM-NEXT:    [[C4:%.*]] = icmp ult i32 [[IV]], [[D_GW_FR]]
; BRANCH_FORM_LICM-NEXT:    [[WIDE_CHK14:%.*]] = and i1 [[WIDE_CHK13]], [[C4]]
; BRANCH_FORM_LICM-NEXT:    [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM_LICM-NEXT:    [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK14]], [[WIDENABLE_COND]]
; BRANCH_FORM_LICM-NEXT:    br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0:![0-9]+]]
; BRANCH_FORM_LICM:       deopt:
; BRANCH_FORM_LICM-NEXT:    call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
; BRANCH_FORM_LICM-NEXT:    ret void
; BRANCH_FORM_LICM:       guarded:
; BRANCH_FORM_LICM-NEXT:    [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM_LICM-NEXT:    [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[C2]], [[WIDENABLE_COND3]]
; BRANCH_FORM_LICM-NEXT:    [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM_LICM-NEXT:    [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[C3]], [[WIDENABLE_COND7]]
; BRANCH_FORM_LICM-NEXT:    [[WIDENABLE_COND11:%.*]] = call i1 @llvm.experimental.widenable.condition()
; BRANCH_FORM_LICM-NEXT:    [[EXIPLICIT_GUARD_COND12:%.*]] = and i1 [[C4]], [[WIDENABLE_COND11]]
; BRANCH_FORM_LICM-NEXT:    [[LOOP_COND:%.*]] = call i1 @cond()
; BRANCH_FORM_LICM-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; BRANCH_FORM_LICM:       exit:
; BRANCH_FORM_LICM-NEXT:    ret void
;
entry:
  br label %loop

loop:
  %iv = phi i32 [0, %entry], [%iv.next, %loop]
  %iv.next = add i32 %iv, 1
  %c1 = icmp ult i32 %iv, %a
  call void(i1, ...) @llvm.experimental.guard(i1 %c1) [ "deopt"() ]
  %c2 = icmp ult i32 %iv, %b
  call void(i1, ...) @llvm.experimental.guard(i1 %c2) [ "deopt"() ]
  %c3 = icmp ult i32 %iv, %c
  call void(i1, ...) @llvm.experimental.guard(i1 %c3) [ "deopt"() ]
  %c4 = icmp ult i32 %iv, %d
  call void(i1, ...) @llvm.experimental.guard(i1 %c4) [ "deopt"() ]
  %loop_cond = call i1 @cond()
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret void
}

declare void @llvm.experimental.guard(i1, ...)
